#title: 动作链
#author: zozoh(zozohtnt@gmail.com)
#index:0,1
------------------------------------------------------------------------------------------
动作链机制概述

    在新的版本中(1.b.36)之后的版本，Nutz.Mvc 统一采用动作链机制来处理每一个 HTTP 请求。
    我们认为:
     * 对于一个 HTTP 请求的处理实际上是由一系列子处理构成的
         * 比如根据映射找到入口函数
         * 比如为入口函数生成调用参数
         * 比如调用入口函数
     * 我们希望这一系列子处理可以根据 URL 的不同而不同
     * 我们也希望子处理是可配置的
    
    因此它能让你的HTTP映射处理具备更大的灵活性。

    下面是一张稍微有点复杂的图，根据这张图，我们来详细的解释一下这个机制，在了解了它之后，
    我相信你掌握更多的复用你代码的手段，从而更合理处理你的 URL 映射关系:
    
    <nutz_mvc_actionchain.png>

    这张图稍微有点复杂，你可能看起来稍微要皱一下眉头。本文的后面几节会详细为你解释，
    但是你首先要记住:
     * 这种图的流程都是 Nutz.Mvc 在{#0000FF;*加载时}进行的操作
     * 在{#008800;*运行时}，Nutz.Mvc 将根据 URL 获得 ActionChain 接口，直接执行整条动作链

------------------------------------------------------------------------------------------
(A)获取动作链工厂
    
    整个 Nutz.Mvc 的应用，{_必须有且只能有一个动作链工厂}。在{*主模块}上，你可以声明你自己的
    动作链工厂（通过 {#AAAAAA;`@ChainBy`} 注解）。当然，如果你没有声明这个注解，Nutz.Mvc 会
    采用默认的动作链工厂实现类({#0000AA;*org.nutz.mvc.impl.NutActionChainMaker})

    如果你需要定制动作链工厂，你可以通过类似下面的形式，声明自己特殊配置的动作链工厂：
    {{{
    @ChainBy(args={"配置文件A路径", "配置文件B路径"})
    }}}
    
    你也可以采用自己的动作链工厂实现类
    {{{
    @ChainBy(type=MyChainMaker.class, args={...})
    }}}
    
    如果你的动作链工厂需要更复杂的配置，你可以交给 Ioc 容器来管理
    {{{
    @ChainBy(type=MyChainMaker.class, args={"ioc:myChianMaker"})
    }}}
    这样，你就可以在 Ioc 容器里，声明一个 "myChainMaker" 对象，来对其做任何你想要的配置。
    当然，首先你需要在主函数里声明了 Ioc 容器（请参看[with_ioc.man 同 Ioc 容器一起工作]一文）



------------------------------------------------------------------------------------------
(B)获取动作链工厂配置信息
    
    如果你采用的是 Nutz.Mvc 的默认动作链工厂，它允许你在构造函数中声明动作链的配置文件。
    你可以增加任意多的动作链配置文件，在一个文件中，你可以声明任意多的动作链。

    它有一个默认的配置文件，声明了一个名字为 "default" 的动作链。你的入口函数如果没有声明
    {#AAAAAA;@Chain} 注解的话，就是使用这个动作链。你可以通过自己的配置文件覆盖它。

    它的构造函数定义为：
    {{{
    public NutActionChainMaker(String...args) {
        ...
    }}}
    接受变参数数组，每个参数，都是你配置文件的路径，可以是类路径，也可以是绝对路径，当然
    你也可以写成:
    {{{
    @ChainBy(args={"${app.root}/WEB-INF/chain/mychain.js"})
    }}}
    其中 `${app.root}` 会被 Nutz.Mvc 替换成你的应用在服务器上的根目录。

------------------------------------------------------------------------------------------
(C)解析配置文件

    每个配置文件你可以配置多个动作链，每个动作链需要一个名字，以便在入口函数通过 
    {#AAAAAA;@Chain}注解来引用，下面让我们来看看默认动作链配置文件的内容：
    {{{
    {
        default : {
            ps : [
                "org.nutz.mvc.impl.processor.UpdateRequestAttributesProcessor",
                "org.nutz.mvc.impl.processor.EncodingProcessor",
                "org.nutz.mvc.impl.processor.ModuleProcessor",
                "org.nutz.mvc.impl.processor.ActionFiltersProcessor",
                "org.nutz.mvc.impl.processor.AdaptorProcessor",
                "org.nutz.mvc.impl.processor.MethodInvokeProcessor",
                "org.nutz.mvc.impl.processor.ViewProcessor"
            ],
            error : 'org.nutz.mvc.impl.processor.FailProcessor'
        }
    }
    }}}
    动作链的配置文件采用了 [http://www.json.org JSON 格式]。在上面的文件内只有一个动作链，
    名字为 {#FF0000;* "default"}。通过例子你可以很容易看出，一个动作链需要两方面的信息：
     * 正常的流程是怎样的？
         * "ps" 属性是一个数组，每个值就是一个处理器接口的实现类
         * 每次该动作链执行时，会按顺序调用这些处理器
     * 遇到错时怎么办？ 
         * 通过给出的错误处理器的实现类来处理错误

------------------------------------------------------------------------------------------
(D)至少还有默认配置文件

    默认的配置文件优先级最低，它随着 nutz.jar 一起发布，所以它没打算让你直接修改。你可以通过
    自己的配置文件覆盖其唯一的动作链 {#FF0000;* "default"}

------------------------------------------------------------------------------------------
(E)为入口函数创建动作链
    
    就像前面提到的，在每个入口函数里，你可以通过注解 {#AAAAAA;@Chain} 来指定你的这个函数将
    采用哪个动作链。如果你没有指定，Nutz.Mvc 认为你是希望用  
    {#AAAAAA;@Chain(}{#FF0000;"default"}{#AAAAAA;)}} 来处理这个入口函数。

    需要说明的是，考虑到效率，在 Nutz.Mvc 加载时，它就会为每个入口函数创建 URL 映射关系。
    即把一个 URL 映射到一个动作链实例上。所以动作链的实例，是在加载时就被创建了。所以如果你自己
    实现了动作链工厂，{#FF0000;*请保证工厂生成的每个动作链是线程安全的}。

------------------------------------------------------------------------------------------
(F)每个入口函数的动作链都可以不同

    如果你读完了上述小节，本节光看标题就足够了。

    但是，我唠叨成性，这里再举个小例子：
    {{{
    @At("/a")
    @Chain("abc")
    public void funcA(){}

    @At("/b")
    @Chain("abc")
    public void funcB(){}

    @At("/c")
    public void funcC(){}

    @At("/d")
    public void funcD(){}
    }}}
    在上面的例子中，四个入口函数，其中：
     * 每个入口函数都各自有一份动作链实例
     * 具体的实例是由动作链工厂决定的
     * 每个动作链实例的生命周期范围是 App(ServletContext) 级别

    原因不解释。

配置示例1,引用ioc里面的bean
------------------------------------------------------

	chain.js的代码
	
	{{{
	{
		"default" : {
			"ps" : [
      			"org.nutz.mvc.impl.processor.UpdateRequestAttributesProcessor",
      			"org.nutz.mvc.impl.processor.EncodingProcessor",
      			"org.nutz.mvc.impl.processor.ModuleProcessor",
      			"ioc:wxProcessor", // 引用一个叫wxProcessor的ioc bean
      			"org.nutz.mvc.impl.processor.ActionFiltersProcessor",
     			 "org.nutz.mvc.impl.processor.AdaptorProcessor",
      			"org.nutz.mvc.impl.processor.MethodInvokeProcessor",
      			"org.nutz.mvc.impl.processor.ViewProcessor"
      		],
			"error" : 'org.nutz.mvc.impl.processor.FailProcessor'
		}
	}
	}}}

	WxProcessor类, 标准的注解式Ioc, 确保IocBy扫描到这个类哦
	
	{{{
	// 注意, 确保是非单例哦
	@IocBean(singleton=false) //默认生成的名字就是类名然后首字母小写,即wxProcessor,对应了chain.js中的配置
	public class WxProcessor extend AbstractProcessor {

    	@Inject Dao dao;
    	@Inject XXXService xxxService;

    	// 其他方法.....
	}
	}}}

配置示例2,不需要ioc注入
-----------------------------------------


	chain.js的代码
	
	{{{
	{
		"default" : {
			"ps" : [
      			"org.nutz.mvc.impl.processor.UpdateRequestAttributesProcessor",
      			"org.nutz.mvc.impl.processor.EncodingProcessor",
      			"org.nutz.mvc.impl.processor.ModuleProcessor",
      			"net.wendal.nutzbook.mvc.LogTimeProcessor", // 直接写类名
      			"org.nutz.mvc.impl.processor.ActionFiltersProcessor",
     			 "org.nutz.mvc.impl.processor.AdaptorProcessor",
      			"org.nutz.mvc.impl.processor.MethodInvokeProcessor",
      			"org.nutz.mvc.impl.processor.ViewProcessor"
      		],
			"error" : 'org.nutz.mvc.impl.processor.FailProcessor'
		}
	}
	}}}

	LogTimeProcessor类
	
	{{{
package net.wendal.bootstrap.mvc;

import javax.servlet.http.HttpServletRequest;

import org.nutz.lang.Stopwatch;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.ActionContext;
import org.nutz.mvc.impl.processor.AbstractProcessor;

public class LogTimeProcessor extends AbstractProcessor {
	
	private static final Log log = Logs.get();

	public LogTimeProcessor() {
	}

	@Override
	public void process(ActionContext ac) throws Throwable {
		Stopwatch sw = Stopwatch.begin();
		try {
			doNext(ac);
		} finally {
			sw.stop();
			if (log.isDebugEnabled()) {
				HttpServletRequest req = ac.getRequest();
				log.debugf("[%-4s]URI=%s %sms", req.getMethod(), req.getRequestURI(), sw.getDuration());
			}
		}
	}

}

	}}}








